/**
 * Copyright (c) 2021-2028, iron.guo 郭成杰 (jiedreams@sina.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.beluga.core.aspect;

import com.beluga.core.annotation.BLimit;
import com.beluga.core.response.ResponseResult;
import com.google.common.util.concurrent.RateLimiter;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author iron.guo
 * @Date 2022/4/28
 * @Description
 */
@Aspect
public class BLimitAspect {

    /**
     * 每秒生成的令牌
     */
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();

    /**
     * @param joinPoint
     * @return
     * @Around(value = "@annotation(com.beluga.core.annotation.BLimit)")
     * 只要使用BLimit注解就会被拦截。
     * 走该环绕通知
     */
    @SneakyThrows
    @Around(value = "@annotation(com.beluga.core.annotation.BLimit)")
    public Object around(ProceedingJoinPoint joinPoint) {

        /**
         * 获取拦截的方法名
         */
        Signature sig = joinPoint.getSignature();
        /**
         * 获取拦截的方法名
         */
        MethodSignature methodSignature = (MethodSignature) sig;
        BLimit bLimit = methodSignature.getMethod().getDeclaredAnnotation(BLimit.class);
        /**
         * 获取到该注解的name
         */
        String value = bLimit.value();
        /**
         * 获取该注解的token
         */
        double token = bLimit.limit();
        /**
         * 判断该名称是否有创建RateLimiter
         */
        RateLimiter rateLimiter = rateLimiters.get(value);
        if (rateLimiter == null) {
            rateLimiter = RateLimiter.create(token);
            rateLimiters.put(value, rateLimiter);
        }
        /**
         * 如果被限流的话
         */
        boolean result = rateLimiter.tryAcquire();
        if (!result) {
            return ResponseResult.Error("当前访问人数过多，请稍后重试!");
        }
        Object proceedResult = joinPoint.proceed();
        return proceedResult;
    }


}
